﻿using Furion.CMS.Core;
using Furion.CMS.Core.Handlers;
using Furion.DatabaseAccessor;
using Furion.DataEncryption.Extensions;
using Furion.DependencyInjection;
using Furion.TaskScheduler;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;

namespace Furion.CMS.Domain
{
    public class UserPassportFormatHandler : AuditLogFormatHandler
    {
        public override string GetRowKey(DbContext context, object entityValues, params string[] formatArgs)
        {
            if (entityValues is PropertyValues values)
            {
                var uid = values.GetValue<int>(nameof(UserPassport.UserId));
                var account = values.GetValue<short>(nameof(UserPassport.Account));

                var user = context.Find<UserInfo>(uid);
                if (user == null) return "未知";

                return $"{user.NickName}-{account}";
            }
            return "未知";
        }
    }

    /// <summary>
    /// 用户通行证
    /// </summary>
    [OperateRecord("用户通行证")]
    public class UserPassport : Entity,
        IEntityTypeBuilder<UserPassport>,
        IEntitySeedData<UserPassport>,
        IEntityChangedListener<UserPassport>
    {
        public UserPassport()
        {
            CreatedTime = DateTimeOffset.UtcNow;
            ExtendedValues = new Dictionary<string, string>();
            LoginRecords = new HashSet<UserLoginRecord>();
        }

        /// <summary>
        /// 用户ID
        /// </summary>
        [Required]
        [OperateRecord("用户ID", true, typeof(UserPassportFormatHandler))]
        public int UserId { get; set; }
        /// <summary>
        /// 关联用户
        /// </summary>
        public virtual UserInfo User { get; set; }
        /// <summary>
        /// 账号
        /// </summary>
        [MaxLength(100), Required]
        public string Account { get; set; }
        /// <summary>
        /// 密码
        /// </summary>
        [MaxLength(50)]
        public string Password { get; set; }
        /// <summary>
        /// 扩展值
        /// </summary>
        public Dictionary<string, string> ExtendedValues { get; set; }
        /// <summary>
        /// 通行证类型
        /// </summary>
        public PassportType Type { get; set; }
        /// <summary>
        /// 通行证区域限制
        /// </summary>
        public PassportRegionRestrict RegionRestrict { get; set; }
        /// <summary>
        /// 是否是种子数据
        /// </summary>
        public bool HasSeed { get; set; }
        /// <summary>
        /// 是否启用
        /// </summary>
        [OperateRecord("是否启用")]
        public bool Enable { get; set; }
        /// <summary>
        /// 登录记录
        /// </summary>
        public virtual ICollection<UserLoginRecord> LoginRecords { get; set; }
        /// <summary>
        /// 通行证白名单
        /// </summary>
        public virtual ICollection<Whitelist> Whitelists { get; set; }

        public void Configure(EntityTypeBuilder<UserPassport> entityBuilder, DbContext dbContext, Type dbContextLocator)
        {
            entityBuilder.UseDefaultTableNameDefine();

            entityBuilder.HasIndex(i => new { i.UserId, i.Account, i.Type });

            entityBuilder.Property(p => p.ExtendedValues)
                .HasConversion(v => JsonConvert.SerializeObject(v), v => JsonConvert.DeserializeObject<Dictionary<string, string>>(v));

            entityBuilder.HasMany(p => p.Whitelists)
                .WithMany(p => p.Passports)
                .UsingEntity<UserPassportRestrict>(
                r => r.HasOne(p => p.Whitelist).WithMany().HasForeignKey(p => p.WhitelistId),
                l => l.HasOne(p => p.Passport).WithMany().HasForeignKey(p => p.PassportId)
                );
        }

        public IEnumerable<UserPassport> HasData(DbContext dbContext, Type dbContextLocator)
        {
            return new List<UserPassport>
            {
                new UserPassport{
                    Id=1,
                    UserId=1,
                    Account = "admin",
                    Password="admin".ToMD5Encrypt(),
                    Type = PassportType.Normal,
                    HasSeed = true,
                    Enable = true
                }
            };
        }

        public void OnChanged(UserPassport newEntity, UserPassport oldEntity, DbContext dbContext, Type dbContextLocator, EntityState state)
        {
            SpareTime.DoIt(() =>
            {
                Scoped.Create((_, scope) =>
                {
                    var services = scope.ServiceProvider;
                    var sessionCaches = services.GetRequiredService<IDistributedCache<long>>();
                    if (newEntity != null)
                    {
                        var key = string.Format(CacheKeyConsts.SessionSafetyIdentify, newEntity.UserId);
                        // 无条件尝试更新安全会话标识
                        if (sessionCaches.Get(key) != 0)
                        {
                            var expire = DateTime.Now.AddHours(12) - DateTime.Now;
                            var sessionId = (newEntity.UpdatedTime ?? newEntity.CreatedTime).ToLong();
                            sessionCaches.SetAsync(key, sessionId, new DistributedCacheEntryOptions
                            {
                                AbsoluteExpirationRelativeToNow = expire
                            });
                        }
                    }
                });
            });
        }
    }
}
